hhkb
웹 개발

블로그_07_보안 점검하기

작성자 : Heehyeon Yoo|2025-12-20
# Blog# Security# Patch# Refactoring# Next.js

명색이 정보보호를 배우는데....

어느 정도 블로그의 완성도도 높였겠다. 명색이 정보보호를 배우고 있는데, 보안 점검을 해보기로 했다.
사실 개인 블로그라 크게 신경 쓰지 않아도 될까 싶었던 부분들인데, 이걸 웹에 올리고 뭐하고 난리부르스를 쳐놨다 보니
헉 소리가 절로 나는 것들이 꽤 있었다.

1. 이미지 API와 경로 탐색 문제

역시나 가장 심각해 보였던 건 이미지 API였다.
api/image?path=... 형태로 이미지를 불러오는데, 이 path에 대한 검증이 거의 없었다.
외부에서 ../../ 같은 상대 경로를 입력하면, 내가 의도한 content 폴더를 벗어나 서버의 시스템 파일까지 접근할 수 있는 어처구니 없이 기초적인 취약점이 있었다.
Path Traversal 공격이 가능할 것이다.
이미지는 블로그에 있으면 보여주는 공개 리소스라 인증은 붙이지 않았다.

경로를 가두자

path.resolve를 사용하여 입력받은 경로를 절대 경로로 변환한 뒤, 그 경로가 반드시 content 디렉토리 안에 포함되어 있는지 확인하는 로직을 추가했다.

const absolutePath = path.resolve(contentDir, inputPath);
if (!absolutePath.startsWith(contentDir)) {
    // 403 Forbidden
}

이제 ..을 아무리 입력해도 정해진 울타리를 벗어날 수 없다.

2. 환경 변수 강제화

JWT 비밀키나 관리자 비밀번호 같은 중요 정보를 코드에 하드코딩하거나, '기본값'을 넣어두는 경우가 있다.
이게 실수로라도 깃허브에 올라가면 바로 사고다. 관리자 정보를 세팅하면서 Gemini 3 Pro를 썼는데,
당신 키를 여기 넣으세요라고 대놓고 하드코딩을 해놨다. 발견했으니 망정이지....

requireEnv 만들기

중요한 설정값은 코드에서 완전히 제거하고, 무조건 환경 변수(env)에서만 가져오도록 변경했다.
requireEnv 유틸리티를 만들어, 서버 시작 시점에 필수 환경 변수가 없으면 아예 에러를 뱉고 서버가 죽도록 만들었다.
배포할 때 설정할 게 늘어서 좀 번거롭긴 하다.

3. 관리자 인증과 타이밍 공격

로그인 로직에서 비밀번호를 비교할 때 단순히 input === password로 비교하고 있었다.
이런 경우는 타이밍 공격(Timing Attack)에 취약하다.
문자열이 틀린 위치에 따라 비교 시간이 미세하게 달라지는데, 이 시간 차이를 측정해서 비밀번호를 유추해낼 수 있다.

timingSafeEqual 함수 사용

비밀번호를 평문으로 비교하는 대신 SHA-256으로 해싱하고, Node.js의 crypto.timingSafeEqual 함수를 사용해 비교하도록 수정했다.
이 함수는 문자열이 달라도 항상 일정한 시간 동안 비교를 수행하기 때문에, 시간 차이를 이용한 공격을 막을 수 있다.

결론

개발에 AI를 적극 활용하는 것은 좋지만, 사람이 꼼꼼히 검토하지 못해 저런 걸 발견하지 못한다면?
이것 또한 휴먼 에러라고 볼 수 있을 것이다. 마무리단에 보안 검토하는 것을 습관화하자....